package com.py.framework.core.filter;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.py.framework.core.common.SpringContextHolder;
import com.py.framework.core.common.URLConstants;
import com.py.framework.core.helper.HttpServletHelper;
import com.py.framework.core.helper.UrlHelper;
import com.py.framework.core.http.HttpAgent;
import com.py.framework.core.log.ExceptionLogger;
import com.py.framework.core.redis.bis.JedisBisManage;
import com.py.framework.core.rest.support.RestServiceResult;
import com.py.framework.core.utils.FileUtils;
import com.py.framework.core.utils.SerializingUtil;

import redis.clients.jedis.Jedis;

/**
 * Token鉴权过滤器
 * @author Leegern
 * @date   2018年4月17日
 */
public class SecurityCheckFilter implements Filter {

	/** 令牌名称 **/
	public static final String Token = "Token";

	/** 资源请求头 **/
	public static final String JD_RESOURCE = "JD-Resource";

	/** 白名单列表 **/
	private Map<String, String> whiteMap;

	/** 鉴权服务地址 **/
	private String checkAuthUrl;

	/** xypj 令牌名称 **/
	public static final String XYPJ_GATEWAY_TOKEN = "XYPJ-Gateway-Token";

	/** 淇＄敤璇勪环鍓嶅彴缃戠珯閰嶇疆url **/
	private List<String> gatewayUrls;

	/** session会话时间 **/
	public static final int SESSION_EXPIRE = 30 * 60;

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
	 * javax.servlet.ServletResponse, javax.servlet.FilterChain)
	 */
	@Override
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
		String servletPath = request.getServletPath();

		if (StringUtils.isBlank(servletPath)) {
			String reqUri = request.getRequestURI();
			String ctxPath = request.getContextPath();
			servletPath = reqUri.replaceFirst(ctxPath, "");
		}

		// 白名单之外的请求都要鉴权
		if (null != this.gatewayUrls && this.isGatewayAccess(servletPath)) {
			// 淇＄敤璇勪环鍓嶅彴缃戠珯鎺ュ彛鎷︽埅
			if (this.isGatewayFilter(request, response, servletPath)) {
				chain.doFilter(req, res);
			}
		} else {
			// 鍏朵粬鍚庡彴鎺ュ彛鎷︽埅
			if (this.isBackgroundFilter(request, response, servletPath)) {
				chain.doFilter(req, res);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
	 */
	@Override
	public void init(FilterConfig config) throws ServletException {
		String whiteStr = config.getInitParameter("whiteList");
		if (StringUtils.isNotBlank(whiteStr)) {
			this.whiteMap = new HashMap<String, String>();
			String[] whiteList = whiteStr.split(",");
			for (String item : whiteList) {
				if (StringUtils.isNotBlank(item)) {
					this.whiteMap.put(item.trim(), item.trim());
				}
			}
		}
		// 閴存潈鏈嶅姟鍦板潃
		this.checkAuthUrl = config.getInitParameter("checkAuthUrl");

		String path = SecurityCheckFilter.class.getResource("/").getFile();
		this.gatewayUrls = FileUtils.readFile(path + "gateway/gatewayUrl.txt");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.servlet.Filter#destroy()
	 */
	@Override
	public void destroy() {

	}

	/**
	 * 输出文本
	 * @param response
	 * @param code
	 * @param msg
	 * @throws IOException
	 */
	private void printMsg(HttpServletResponse response, int code, String msg) throws IOException {
		// 设置格式为text/json
		response.setContentType("text/json");
		// 设置字符集为'UTF-8'
		response.setCharacterEncoding("UTF-8");

		// 设置返回信息
		RestServiceResult<String> result = new RestServiceResult<String>();
		result.setCode(code);
		result.setMsg(msg);

		PrintWriter out = response.getWriter();
		out.println(JSON.toJSONString(result));
		out.flush();
		out.close();
	}

	/**
	 * 鍚庡彴鍔熻兘妯″潡鎷︽埅杩囨护
	 * 
	 * @param request
	 * @param response
	 * @param servletPath
	 * @return
	 * @throws IOException
	 * @throws ServletException
	 */
	private boolean isBackgroundFilter(HttpServletRequest request, HttpServletResponse response, String servletPath)
			throws IOException, ServletException {

		if (!servletPath.contains("webjars") && !servletPath.contains("swagger")
				&& !servletPath.contains("/v2/api-docs") && !servletPath.contains("/account/front/")
				&& !servletPath.contains("/org/front/") && !servletPath.contains("/activity/front/")
				&& !servletPath.contains("/model/front/") && !servletPath.contains("/mul/front/")
				&& !servletPath.contains("/facade/front/")) {
			// 白名单之外的请求都要鉴权
			if (null == this.whiteMap || !this.whiteMap.containsKey(servletPath)) {
				/*
				 * 首先从cookie中查找令牌, 如果不存在, 再从请求头中查找.
				 */
				String jdToken = HttpServletHelper.getUserToken(request);
				// token校验┖
				if (StringUtils.isBlank(jdToken)) {
					// 输出响应信息
					this.printMsg(response, RestServiceResult.CODE_LOGOUT, "此用户未登录5！");
					return false;
				}
				// 鉴权地址是否为空
				if (StringUtils.isBlank(this.checkAuthUrl)) {
					UrlHelper urlHelper = SpringContextHolder.getBean(UrlHelper.class);
					if (null != urlHelper && StringUtils.isNotBlank(urlHelper.getBaseServerUrl())) {
						this.checkAuthUrl = urlHelper.getBaseServerUrl() + URLConstants.CHECK_RESOURCE_URL;
					} else {
						// 输出响应信息
						this.printMsg(response, RestServiceResult.CODE_UNKNOW, "閴存潈鏈嶅姟鍦板潃涓虹┖锛�");
						return false;
					}
				}
				// 设置请求头信息
				Map<String, String> headers = new HashMap<>();
				headers.put(Token, jdToken);
				headers.put(JD_RESOURCE, servletPath);
				// 楠岃瘉鏄惁涓烘湁鏁堢敤鎴�
				try {
					// String result =
					// HttpAgent.getInstance().sendHttpPost(this.checkAuthUrl,
					// jdToken);
					String result = HttpAgent.getInstance().sendHttpPost(this.checkAuthUrl, "resource=" + servletPath,
							headers);
					if (StringUtils.isNotBlank(result)) {
						RestServiceResult<String> json = JSON.parseObject(result,
								new TypeReference<RestServiceResult<String>>() {
								});
						if (null != json) {
							if (json.getCode() == RestServiceResult.CODE_LOGOUT) {
								// 输出响应信息
								this.printMsg(response, RestServiceResult.CODE_LOGOUT, "此用户未登录6！");
								return false;
							} else if (json.getCode() == RestServiceResult.CODE_NOAUTH) {
								// 输出响应信息
								this.printMsg(response, RestServiceResult.CODE_NOAUTH, "用户无权限");
								return false;
							} else if (json.getCode() == RestServiceResult.CODE_UNKNOW) {
								// 输出响应信息
								this.printMsg(response, RestServiceResult.CODE_UNKNOW, "未知错误");
								return false;
							}
						} else {
							// 输出响应信息
							this.printMsg(response, RestServiceResult.CODE_UNKNOW, "未知错误");
							return false;
						}
					} else {
						// 输出响应信息
						this.printMsg(response, RestServiceResult.CODE_UNKNOW, "未知错误");
						return false;
					}
				} catch (Exception e) {
					ExceptionLogger.error(e);
					// 输出响应信息
					this.printMsg(response, RestServiceResult.CODE_UNKNOW, "未知错误");
					return false;
				}
			}
		}

		return true;
	}

	/**
	 * 鍓嶅彴淇＄敤璇勪环缃戠珯鎷︽埅杩囨护
	 * 
	 * @return
	 */
	private boolean isGatewayFilter(HttpServletRequest request, HttpServletResponse response, String servletPath)
			throws IOException, ServletException {
		/*
		 * 首先从cookie中查找令牌, 如果不存在, 再从请求头中查找.
		 */
		String xypjGatewayToken = HttpServletHelper.getUserXypjGatewayToken(request);
		// token校验
		if (StringUtils.isBlank(xypjGatewayToken)) {
			// 输出响应信息
			this.printMsg(response, RestServiceResult.CODE_LOGOUT, "此用户未登录7！");
			return false;
		}

		if (!this.checkTokenIsLoginUser(xypjGatewayToken)) {
			// 输出响应信息
			this.printMsg(response, RestServiceResult.CODE_LOGOUT, "此用户未登录8！");
			return false;
		}

		// 设置请求头信息
		Map<String, String> headers = new HashMap<>();
		headers.put(XYPJ_GATEWAY_TOKEN, xypjGatewayToken);

		return true;
	}

	/**
	 * 鍒ゆ柇token鏄惁鏄櫥褰曠姸鎬佺殑鐢ㄦ埛
	 * 
	 * @param jdToken
	 * @return
	 */
	private synchronized boolean checkTokenIsLoginUser(String jdToken) {
		boolean result = false;
		Jedis jedis = null;
		try {
			jedis = JedisBisManage.getJedis();
			result = jedis.exists(SerializingUtil.serialize(jdToken));
			// 濡傛灉瀛樺湪,閲嶇疆杩囨湡鏃堕棿.
			if (result) {
				jedis.expire(jdToken, SecurityCheckFilter.SESSION_EXPIRE);
			}
		} catch (Exception e) {
			ExceptionLogger.error(e);
		} finally {
			if (null != jedis) {
				JedisBisManage.returnResource(jedis);
			}
		}
		return result;
	}

	/**
	 * 鍒ゆ柇璧勬簮鏄惁鏄俊鐢ㄨ瘎浠峰墠鍙扮綉绔�
	 * 
	 * @param resource
	 *            璧勬簮鍦板潃
	 * @return
	 */
	private boolean isGatewayAccess(String resource) {
		boolean flag = false;

		if (this.gatewayUrls.contains(resource)) {
			flag = true;
		} else {
			for (String key : this.gatewayUrls) {
				if (key.endsWith("/*")) {
					key = key.substring(0, (key.length() - 1));
					if (resource.startsWith(key)) {
						flag = true;
						break;
					}
				}
			}
		}
		return flag;
	}
}